#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
# @version: v1.1
# @author: cd
# @Email: 19688513@qq.com
# @Project: new-horizons-engine
# @File: DatabaseManager.py
# @Software: PyCharm
# @time: 2025/5/30 9:37
# @description: 负责数据库的创建和配置
# 封装了数据库表结构的初始化逻辑
# 提供数据库连接管理
# 包含数据库性能优化配置
"""

import urllib3
import logging
import sqlite3
import os
from PathManager import PathManager
from datetime import datetime, date, time

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# 禁用不安全的请求警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


class DatabaseManager:
    """数据库管理类，负责数据库的创建和配置"""

    def __init__(self, db_name: str = 'stock_history_data.db'):
        """初始化数据库管理器

        Args:
            db_name (str): 存储数据库的名称，默认为 'stock_history_data.db'
        """
        pm = PathManager()  # 创建路径管理实例
        self.db_name = pm.get_database_path(db_name)  # 获取数据库的完整路径
        self.create_database()  # 创建数据库及表结构
        self.configure_database()  # 配置数据库性能参数

    def create_database(self) -> None:
        """确保数据库和所有表结构存在"""
        try:
            with sqlite3.connect(self.db_name) as conn:
                # 无论数据库是否已存在，都创建或更新表结构
                self._create_basic_table(conn)  # 创建基本表
                conn.commit()  # 提交创建事务
                logging.info(f"数据库表结构已确认: {self.db_name}")
        except sqlite3.Error as e:
            logging.critical(f"数据库创建失败: {e}")
            if os.path.exists(self.db_name):
                os.remove(self.db_name)  # 如果创建失败，删除数据库文件
            raise

    def _create_basic_table(self, conn: sqlite3.Connection) -> None:
        """创建或更新所有必要的表结构

        Args:
            conn (sqlite3.Connection): 数据库连接对象
        """
        # 创建股票基本信息表
        conn.execute('''
            CREATE TABLE IF NOT EXISTS stock_basic_info (
                stock_code TEXT PRIMARY KEY,  -- 股票代码
                stock_name TEXT,               -- 股票名称
                total_market_value REAL,       -- 总市值
                circulating_market_value REAL,  -- 流通市值
                industry TEXT,                 -- 行业
                listing_date TIMESTAMP,             -- 上市日期
                total_shares INTEGER,          -- 总股本
                circulating_shares INTEGER,     -- 流通股本
                rating_agency TEXT,            -- 评级机构
                rating REAL,                   -- 评级
                rating_quarter TEXT,           -- 评级季度
                market_type TEXT,              -- 市场类型
                last_updated TEXT               -- 最后更新日期
            )
        ''')

        # 创建按年分表的股票数据表
        current_year = datetime.now().year  # 获取当前年份
        for year in range(2007, current_year + 1):
            self._create_year_table(conn, year)  # 创建每年数据表

        # 创建交易日期表
        self._create_trade_table(conn)  # 创建交易日期表

    def _create_trade_table(self, conn: sqlite3.Connection) -> None:
        """创建交易日期表

        Args:
            conn (sqlite3.Connection): 数据库连接对象
        """
        conn.execute('''
            CREATE TABLE IF NOT EXISTS trade_dates (
                trade_date TIMESTAMP PRIMARY KEY  -- 交易日期
            )
        ''')
        logging.info("交易日期表已确认")

    def _create_year_table(self, conn: sqlite3.Connection, year: int) -> None:
        """创建指定年份的数据表

        Args:
            conn (sqlite3.Connection): 数据库连接对象
            year (int): 年份
        """
        table_name = f'stock_data_{year}'
        # 创建表时使用symbol和date的复合主键
        conn.execute(f'''
            CREATE TABLE IF NOT EXISTS {table_name} (
                date TIMESTAMP NOT NULL,            -- 日期，以TEXT格式存储
                symbol TEXT NOT NULL,          -- 股票代码
                open REAL,                     -- 开盘价
                close REAL,                    -- 收盘价
                high REAL,                     -- 最高价
                low REAL,                      -- 最低价
                volume REAL,                   -- 成交量
                turnover REAL,                 -- 成交额
                amplitude REAL,                -- 振幅
                change_pct REAL,               -- 涨跌幅
                change_amt REAL,               -- 涨跌额
                turnover_rate REAL,            -- 换手率
                PRIMARY KEY (symbol, date)     -- 使用symbol和date作为复合主键
            ) WITHOUT ROWID
        ''')
        # 创建索引以加速查询
        conn.execute(f'CREATE INDEX IF NOT EXISTS idx_{year}_symbol ON {table_name}(symbol)')
        conn.execute(f'CREATE INDEX IF NOT EXISTS idx_{year}_date ON {table_name}(date)')
        conn.execute(f'CREATE INDEX IF NOT EXISTS idx_{year}_symbol_date ON {table_name}(symbol, date)')
        logging.debug(f"年份数据表 {table_name} 已确认")

    def configure_database(self) -> None:
        """配置数据库参数以优化性能"""
        try:
            with sqlite3.connect(self.db_name) as conn:
                conn.execute("PRAGMA journal_mode = WAL;")  # 使用WAL模式提升并发性能
                conn.execute("PRAGMA synchronous = NORMAL;")  # 设置同步模式
                conn.execute("PRAGMA cache_size = -20000;")  # 设置缓存大小
                conn.execute("PRAGMA mmap_size = 1073741824;")  # 设置内存映射大小
                conn.execute("PRAGMA temp_store = MEMORY;")  # 将临时表存储在内存中
                conn.execute("PRAGMA busy_timeout = 30000;")  # 设置忙碌超时
                conn.execute("PRAGMA locking_mode = EXCLUSIVE;")  # 提升写入性能
                conn.execute("PRAGMA foreign_keys = ON;")  # 启用外键约束
                conn.execute("PRAGMA auto_vacuum = FULL;")  # 自动维护数据库文件大小
                logging.info("数据库性能配置已应用")
        except sqlite3.Error as e:
            logging.error(f"数据库配置失败: {e}")

    def _register_date_adapters(self) -> None:
        """注册日期类型适配器和转换器（全局设置）"""

        # 自定义日期适配器（将日期对象转为ISO字符串）
        def adapt_datetime(obj):
            if isinstance(obj, (datetime, date, time)):
                return obj.isoformat()
            raise TypeError(f"Unsupported type for SQLite adapter: {type(obj)}")

        # 自定义日期转换器（将ISO字符串转回日期对象）
        def convert_datetime(s):
            return datetime.fromisoformat(s.decode("utf-8")) if s else None  # 处理 None 值

        # 注册适配器和转换器（全局有效）
        sqlite3.register_adapter((datetime, date, time), adapt_datetime)
        sqlite3.register_converter("TIMESTAMP", convert_datetime)

    def get_connection(self) -> sqlite3.Connection:
        """获取配置好的数据库连接"""
        self._register_date_adapters()  # 注册适配器（确保全局生效）

        # 创建连接并启用类型检测
        conn = sqlite3.connect(
            self.db_name,
            detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES  # 检测列类型和声明类型
        )

        # 应用性能配置（每个连接独立配置）
        conn.execute("PRAGMA journal_mode = WAL;")
        conn.execute("PRAGMA synchronous = NORMAL;")
        conn.execute("PRAGMA cache_size = -20000;")
        conn.execute("PRAGMA mmap_size = 1073741824;")
        conn.execute("PRAGMA temp_store = MEMORY;")
        conn.execute("PRAGMA busy_timeout = 30000;")
        conn.execute("PRAGMA foreign_keys = ON;")

        return conn

    def delete_database(self) -> None:
        """删除数据库文件"""
        try:
            if os.path.exists(self.db_name):
                os.remove(self.db_name)
                logging.info(f"数据库 {self.db_name} 已删除")
            else:
                logging.warning(f"数据库 {self.db_name} 不存在")
        except Exception as e:
            logging.error(f"删除数据库失败: {e}")